﻿namespace com.crownpack.convert
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading;

    using com.crownpack;
    using com.crownpack.Extensions.DoubleExtensions;
    using com.crownpack.Extensions.EventHandlerExtensions;
    using com.crownpack.Extensions.SharePointExtensions;
    using com.crownpack.Extensions.StringExtensions;
    using com.crownpack.InitiativesDao;

    using Microsoft.SharePoint;

    internal class Convert
    {
        #region Fields

        InitiativesConfiguration _configuration;
        int _internalID;
        Guid? _LocationFieldId;
        List<Customer> _Locations;
        Dictionary<string, string> _Xlat;

        #endregion Fields

        #region Events

        public event EventHandler<ItemsConvertedEventArgs> ItemsConverted;

        public event EventHandler SynchronizationStarted;

        #endregion Events

        #region Properties

        public int ParentCustomerNumber
        {
            get;
            set;
        }

        public Uri SourceUri
        {
            get;
            set;
        }

        public string TargetList
        {
            get;
            set;
        }

        public Uri TargetUri
        {
            get;
            set;
        }

        InitiativesConfiguration configuration
        {
            get
            {
                if ( _configuration == null )
                {
                    InitiativesConfigurationDao dao = new InitiativesConfigurationDao( TargetUri.ToString( ) );
                    _configuration = dao.GetByClientSite( GetClientSiteUrl( TargetUri ) );
                }
                return _configuration;
            }
        }

        int currentID
        {
            get;
            set;
        }

        int internalID
        {
            get
            {
                return _internalID;
            }
            set
            {
                if ( value > _internalID )
                    _internalID = value;
            }
        }

        Guid LocationFieldId
        {
            get
            {
                if ( _LocationFieldId == null )
                {
                    using ( SPSite spSite = new SPSite( TargetUri.ToString( ) ) )
                    {
                        using ( SPWeb spWeb = spSite.OpenWeb( ) )
                        {
                            SPList spList = spWeb.Lists.GetListByRootFolder( TargetList );
                            SPField spField = spList.Fields.GetFieldByInternalName( Column.Location );
                            if ( spField != null )
                                _LocationFieldId = spField.Id;
                        }
                    }
                }
                return _LocationFieldId.HasValue ? _LocationFieldId.Value : Guid.Empty;
            }
        }

        List<Customer> Locations
        {
            get
            {
                if ( _Locations == null )
                {
                    if ( configuration != null && configuration.Active.HasValue && configuration.Active.Value )
                    {
                        CustomerDao dao = new CustomerDao( TargetUri.ToString( ) );
                        _Locations = dao.GetByParentCustomerNo( configuration.ParentCustomerNo.Value.ToInt32( ), configuration.SourceList );
                    }
                }
                return _Locations;
            }
        }

        Dictionary<string, string> Xlat
        {
            get
            {
                if ( _Xlat == null )
                {
                    _Xlat = new Dictionary<string, string>( );
                    _Xlat.Add( "Title", "Title" );
                    _Xlat.Add( OrigColumn.Archived, Column.Archived );
                    _Xlat.Add( OrigColumn.CostSavingsType, Column.CostSavingsType );
                    _Xlat.Add( OrigColumn.CustomerInitiativeNumber, Column.CustomerInitiativeNumber );
                    _Xlat.Add( OrigColumn.CustomerPartNo, Column.CustomerPartNo );
                    _Xlat.Add( OrigColumn.CustomerPartNoDescription, Column.CustomerPartNoDescription );
                    _Xlat.Add( OrigColumn.EAU, Column.EAU );
                    _Xlat.Add( OrigColumn.EstimatedTotalSavingsTotal, Column.EstimatedTotalSavingsTotal );
                    _Xlat.Add( OrigColumn.InitiativeDetail, Column.InitiativeDetail );
                    _Xlat.Add( OrigColumn.InitiativeNo, Column.InitiativeNo );
                    _Xlat.Add( OrigColumn.InitiativeTimeFrame, Column.InitiativeTimeFrame );
                    _Xlat.Add( OrigColumn.ItemDescriptionOfNewItem, Column.ItemDescriptionOfNewItem );
                    _Xlat.Add( OrigColumn.LocationName, Column.LocationName );
                    _Xlat.Add( OrigColumn.NewItemNo, Column.NewItemNo );
                    _Xlat.Add( OrigColumn.NewPrice, Column.NewPrice );
                    _Xlat.Add( OrigColumn.OriginalItemNo, Column.OriginalItemNo );
                    _Xlat.Add( OrigColumn.OriginalPrice, Column.OriginalPrice );
                    _Xlat.Add( OrigColumn.ParentCustomerNo, Column.ParentCustomerNo );
                    _Xlat.Add( OrigColumn.PlantChampion, Column.PlantChampion );
                    _Xlat.Add( OrigColumn.PlantChampionEmail, Column.PlantChampionEmail );
                    _Xlat.Add( OrigColumn.PlantChampionTelephone, Column.PlantChampionTelephone );
                    _Xlat.Add( OrigColumn.PlantChampionTitle, Column.PlantChampionTitle );
                    _Xlat.Add( OrigColumn.ProductCategory, Column.ProductCategory );
                    _Xlat.Add( OrigColumn.RealizationDate, Column.RealizationDate );
                    _Xlat.Add( OrigColumn.RejectDate, Column.RejectionDate );
                    _Xlat.Add( OrigColumn.RejectionReason, Column.RejectionReason );
                    _Xlat.Add( OrigColumn.Status, Column.Status );
                    _Xlat.Add( OrigColumn.UnitOfMeasure, Column.UnitOfMeasure );
                    _Xlat.Add( OrigColumn.UnitSavings, Column.UnitSavings );
                }
                return _Xlat;
            }
        }

        #endregion Properties

        #region Methods

        public static bool DoesConfigurationExists( Uri targetUri )
        {
            InitiativesConfigurationDao dao = new InitiativesConfigurationDao( targetUri.ToString( ) );
            var configuration = dao.GetByClientSite( GetClientSiteUrl( targetUri ) );
            return configuration != null;
        }

        public static bool DoesExportFileExists( Uri sourceUri )
        {
            using ( SPSite spSite = new SPSite( sourceUri.ToString( ) ) )
            {
                using ( SPWeb spWeb = spSite.OpenWeb( ) )
                {
                    SPFile spFile = spWeb.GetFile( sourceUri.ToString( ) );
                    return spFile != null && spFile.Exists;
                }
            }
        }

        public static bool DoesTargetListExists( Uri targetUri, string targetList )
        {
            using ( SPSite spSite = new SPSite( targetUri.ToString( ) ) )
            {
                using ( SPWeb spWeb = spSite.OpenWeb( ) )
                {
                    SPList spList = spWeb.Lists.GetListByRootFolder( targetList );
                    return spList != null;
                }
            }
        }

        public static string GetClientSiteUrl( Uri targetUri )
        {
            using ( SPSite spSite = new SPSite( targetUri.ToString( ) ) )
            {
                using ( SPWeb spWeb = spSite.OpenWeb( ) )
                {
                    return spWeb.ParentWeb.Url;
                }
            }
        }

        public static bool IsConfigurationActive( Uri targetUri )
        {
            InitiativesConfigurationDao dao = new InitiativesConfigurationDao( targetUri.ToString( ) );
            var configuration = dao.GetByClientSite( GetClientSiteUrl( targetUri ) );
            return configuration != null && configuration.Active.HasValue && configuration.Active.Value;
        }

        public static bool IsParentCustomerNoValid( Uri targetUri, int parentCustomerNo )
        {
            InitiativesConfigurationDao dao = new InitiativesConfigurationDao( targetUri.ToString( ) );
            var configuration = dao.GetByClientSite( GetClientSiteUrl( targetUri ) );
            return configuration != null && configuration.ParentCustomerNo.HasValue && configuration.ParentCustomerNo.Value.ToInt32( ).Equals( parentCustomerNo );
        }

        public int Execute( )
        {
            int numberItems = 0;
            using ( SPSite spSite = new SPSite( TargetUri.ToString( ) ) )
            {
                using ( SPWeb spWeb = spSite.OpenWeb( ) )
                {
                    SPList spList = spWeb.Lists.GetListByRootFolder( TargetList );
                    if ( spList == null )
                        //throw new Exception( string.Format( "Target list '{0}' not found at {1}.", TargetList, spWeb.Url ) );
                        return ExitCode.TargetListNotFound;

                    if ( configuration == null )
                        //throw new Exception( string.Format( "Configuration for {0} was not found.", GetClientSiteUrl( TargetUri ) ) );
                        return ExitCode.ConfigurationNotFound;

                    if ( configuration.Active.HasValue && !configuration.Active.Value )
                        //throw new Exception( string.Format( "Configuration for {0} was found, but is not active.", configuration.ClientSiteUrl ) );
                        return ExitCode.ConfigurationNotActive;

                    if ( configuration.ParentCustomerNo.HasValue && !configuration.ParentCustomerNo.Value.ToInt32( ).Equals( ParentCustomerNumber ) )
                        //throw new Exception( string.Format( "Configuration for {0} was found, but the configured parent customer number does not match the specified.", configuration.ClientSiteUrl ) );
                        return ExitCode.ParentCustomerNoInvalid;

                    spList.Clear( );

                    Dictionary<string, Guid> resolvedXlat = new Dictionary<string, Guid>( );
                    foreach ( Dictionary<string, string> v in readTsv( ) )
                    {
                        int? locationNo = null;
                        {
                            string pcn = string.Empty;
                            bool ok = v.TryGetValue( OrigColumn.ParentCustomerNo, out pcn );
                            if ( ok )
                            {
                                int i = pcn.ToInt32( );
                                if ( i != ParentCustomerNumber )
                                    break;
                            }
                        }

                        SPListItem spListItem = spList.AddItem( );

                        numberItems++;
                        if ( numberItems % 10 == 0 )
                        {
                            ItemsConverted.TryRaise( this, new ItemsConvertedEventArgs( numberItems ) );
                            Thread.Sleep( 5000 );
                        }

                        foreach ( KeyValuePair<string, string> kvp in v )
                        {
                            if ( Xlat.ContainsKey( kvp.Key ) )
                            {
                                if ( !resolvedXlat.ContainsKey( kvp.Key ) )
                                {
                                    string s = string.Empty;
                                    bool ok = Xlat.TryGetValue( kvp.Key, out s );
                                    if ( ok )
                                    {
                                        SPField spfield = spList.Fields.GetFieldByInternalName( s );
                                        if ( spfield != null )
                                            resolvedXlat.Add( kvp.Key, spfield.Id );
                                    }
                                }
                                if ( resolvedXlat.ContainsKey( kvp.Key ) )
                                {
                                    Guid g = Guid.Empty;
                                    bool ok = resolvedXlat.TryGetValue( kvp.Key, out g );
                                    if ( ok )
                                    {
                                        switch ( kvp.Key )
                                        {
                                            case OrigColumn.Archived:
                                                spListItem[ g ] = kvp.Value.ToNullableBoolean( );
                                                break;
                                            case OrigColumn.LocationName:
                                                {
                                                    locationNo = getLocationNo( kvp.Value );
                                                    if ( locationNo.HasValue )
                                                        spListItem[ LocationFieldId ] = CustomerFieldValue.ToCustomerFieldValue( ParentCustomerNumber, locationNo.Value, kvp.Value );
                                                }
                                                spListItem[ g ] = kvp.Value;
                                                break;
                                            case OrigColumn.EAU:
                                            case OrigColumn.EstimatedTotalSavingsTotal:
                                            case OrigColumn.InitiativeTimeFrame:
                                            case OrigColumn.NewPrice:
                                            case OrigColumn.OriginalPrice:
                                            case OrigColumn.UnitSavings:
                                                spListItem[ g ] = kvp.Value.ToNullableDouble( );
                                                break;
                                            case OrigColumn.ParentCustomerNo:
                                                spListItem[ g ] = kvp.Value.ToInt32( );
                                                break;
                                            case OrigColumn.RealizationDate:
                                            case OrigColumn.RejectDate:
                                                spListItem[ g ] = kvp.Value.ToNullableDateTime( );
                                                break;
                                            case OrigColumn.InitiativeNo:
                                                spListItem[ g ] = kvp.Value;
                                                string[ ] s = kvp.Value.Split( '-' );
                                                if ( s.Length >= 2 )
                                                    internalID = s[ 1 ].ToInt32( );
                                                break;
                                            default:
                                                spListItem[ g ] = kvp.Value;
                                                break;
                                        }
                                    }
                                }
                            }
                        }

                        spListItem[ "ContentTypeId" ] = getContentTypeId( spListItem, locationNo );
                        spListItem.SystemUpdate( false );
                        spListItem = spList.Items[ spListItem.UniqueId ];
                        currentID = spListItem.ID;
                        //if ( locationNo.HasValue )
                        //{
                        //    Initiative initiative = new Initiative( spListItem, SPEventType.Add );
                        //    initiative.SystemUpdate( );
                        //}
                    }
                }
            }

            Thread.Sleep( 10000 );

            SynchronizationStarted.TryRaise( this );
            syncCurrentWithInternalID( );

            Thread.Sleep( 10000 );

            ItemsConverted.TryRaise( this, new ItemsConvertedEventArgs( numberItems ) );

            return ExitCode.OK;
        }

        string getContentTypeId( SPListItem spListItem, int? locationNo )
        {
            if ( !locationNo.HasValue || ( locationNo.HasValue && locationNo.Value <= 0 ) )
                return Final_Definition_SiteLevel.UnknownInitiative.ContentTypeId;

            var costSavingType = spListItem.GetValue<string>( Column.CostSavingsType );
            if ( string.IsNullOrEmpty( costSavingType ) )
                return Final_Definition_SiteLevel.UnknownInitiative.ContentTypeId;

            var status = spListItem.GetValue<string>( Column.Status );
            var contentTypeSuffix = string.Empty;
            if ( status.Equals( Status.Implemented, StringComparison.OrdinalIgnoreCase ) )
                contentTypeSuffix = Status.Implemented;
            else if ( status.Equals( Status.InProcess, StringComparison.OrdinalIgnoreCase ) )
                contentTypeSuffix = Status.InProcess;
            else if ( status.Equals( Status.Proposed, StringComparison.OrdinalIgnoreCase ) )
                contentTypeSuffix = Status.Proposed;
            else
                contentTypeSuffix = Status.Proposed;

            var contentTypePrefix = string.Empty;
            if ( costSavingType.Equals( ContentTypeName.CostAvoidance, StringComparison.OrdinalIgnoreCase ) )
                contentTypePrefix = ContentTypeName.CostAvoidance;
            else if ( costSavingType.Equals( ContentTypeName.CostReduction, StringComparison.OrdinalIgnoreCase ) )
                contentTypePrefix = ContentTypeName.CostReduction;
            else if ( costSavingType.Equals( ContentTypeName.CustomerProvided, StringComparison.OrdinalIgnoreCase ) )
                contentTypePrefix = ContentTypeName.CustomerProvided;
            else if ( costSavingType.Equals( ContentTypeName.ProductRedesign, StringComparison.OrdinalIgnoreCase ) )
                contentTypePrefix = ContentTypeName.ProductRedesign;
            else
                contentTypePrefix = ContentTypeName.CostReduction;

            var contentTypeName = string.Format( "{0}-{1}", contentTypePrefix, contentTypeSuffix );

            var contentTypeId = string.Empty;
            if ( contentTypeName.Equals( ContentTypeName.CostAvoidanceImplemented, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostAvoidanceImplemented.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CostAvoidanceInProcess, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostAvoidanceInProcess.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CostAvoidanceProposed, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostAvoidanceProposed.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CostReductionImplemented, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostReductionImplemented.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CostReductionInProcess, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostReductionInProcess.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CostReductionProposed, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CostReductionProposed.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CustomerProvidedImplemented, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CustomerProvidedImplemented.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CustomerProvidedInProcess, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CustomerProvidedInProcess.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.CustomerProvidedProposed, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.CustomerProvidedProposed.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.ProductRedesignImplemented, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.ProductRedesignImplemented.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.ProductRedesignInProcess, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.ProductRedesignInProcess.ContentTypeId;
            else if ( contentTypeName.Equals( ContentTypeName.ProductRedesignProposed, StringComparison.OrdinalIgnoreCase ) )
                contentTypeId = Final_Definition_SiteLevel.ProductRedesignProposed.ContentTypeId;
            else
                contentTypeId = Final_Definition_SiteLevel.UnknownInitiative.ContentTypeId;

            return contentTypeId;
        }

        int? getLocationNo( string locationName )
        {
            Customer customer = Locations.Find( a => a.LocationName.Equals( locationName, StringComparison.OrdinalIgnoreCase ) );
            return customer != null ? customer.LocationNo : null;
        }

        IEnumerable readTsv( )
        {
            using ( SPSite spSite = new SPSite( SourceUri.ToString( ) ) )
            {
                using ( SPWeb spWeb = spSite.OpenWeb( ) )
                {
                    SPFile spFile = spWeb.GetFile( SourceUri.ToString( ) );
                    using ( StreamReader reader = new StreamReader( spFile.OpenBinaryStream( ) ) )
                    {
                        string[ ] h = new string[ 0 ];
                        while ( !reader.EndOfStream )
                        {
                            string contents = reader.ReadLine( );
                            if ( h.Length == 0 )
                                h = contents.Split( '\t' );
                            else
                            {
                                string[ ] v = contents.Split( '\t' );
                                if ( h.Length != v.Length )
                                    yield break;

                                Dictionary<string, string> d = new Dictionary<string, string>( );
                                for ( int i = 0; i < h.Length; i++ )
                                    d.Add( h[ i ], v[ i ] );
                                yield return d;
                            }
                        }
                    }
                }
            }
        }

        void syncCurrentWithInternalID( )
        {
            int i = internalID - currentID;
            for ( int j = 0; j < i; j++ )
            {
                using ( SPSite spSite = new SPSite( TargetUri.ToString( ) ) )
                {
                    using ( SPWeb spWeb = spSite.OpenWeb( ) )
                    {
                        SPList spList = spWeb.Lists.GetListByRootFolder( TargetList );
                        SPListItem spListItem = spList.AddItem( );
                        spListItem.SystemUpdate( );
                        spList.Items.DeleteItemById( spListItem.ID );
                    }
                }
            }
        }

        #endregion Methods

        #region Nested Types

        public class ExitCode
        {
            #region Fields

            public const int ConfigurationNotActive = 8;
            public const int ConfigurationNotFound = 4;
            public const int ExportFileNotFound = 2;
            public const int OK = 0;
            public const int ParentCustomerNoInvalid = 16;
            public const int TargetListNotFound = 64;
            public const int TargetSiteUrlNotFound = 32;

            #endregion Fields
        }

        public class ItemsConvertedEventArgs : EventArgs
        {
            #region Constructors

            public ItemsConvertedEventArgs( int numberItems )
            {
                NumberItems = numberItems;
            }

            #endregion Constructors

            #region Properties

            public int NumberItems
            {
                get;
                private set;
            }

            #endregion Properties
        }

        #endregion Nested Types
    }
}